package cn.mw.cmdb.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.mw.cmdb.entity.DataAuth;
import cn.mw.cmdb.entity.InstanceInfoExtend;
import cn.mw.cmdb.entity.InventoryWorkAssets;
import cn.mw.cmdb.entity.InventoryWorkInfo;
import cn.mw.cmdb.entity.dto.InventoryHistoryDetailsDTO;
import cn.mw.cmdb.enums.InventoryCheck;
import cn.mw.cmdb.enums.WorkStateEnum;
import cn.mw.cmdb.mongoMapper.InventoryWorkAssetsMapper;
import cn.mw.cmdb.mongoMapper.InventoryWorkInfoMapper;
import cn.mw.cmdb.param.InstanceSearchParam;
import cn.mw.cmdb.param.InventoryAssetsIdsParam;
import cn.mw.cmdb.param.InventorySearchParam;
import cn.mw.cmdb.param.InventoryWorkAssetsParam;
import cn.mw.cmdb.service.InventoryWorkAssetsService;
import cn.mw.cmdb.util.CMDBTool;
import cn.mw.cmdb.view.ViewConvertContext;
import cn.mw.cmdb.view.ViewManager;
import cn.mw.components.mongodb.exception.BusinessException;
import cn.mw.components.mongodb.page.MWPage;
import cn.mw.components.mongodb.tool.CriteriaWrapper;
import cn.mw.microMonitorCommon.api.user.MwUserApi;
import cn.mw.microMonitorCommon.entity.userDTO.OrgDTO;
import cn.mw.microMonitorCommon.security.dto.MwUser;
import cn.mw.microMonitorCommon.security.utils.SecurityUtils;
import cn.mw.microMonitorCommon.util.DateUtils;
import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.core.query.Update;

import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Function;
import java.util.stream.Collectors;

import static cn.mw.cmdb.service.impl.InventoryWorkInfoServiceImpl.*;
import static cn.mw.cmdb.util.BaseUtil.getNowLocalTime;
import static cn.mw.cmdb.util.ValConvertUtil.strValueConvert;

/**
 * @Author shenwenyi
 * @Date 2024/6/4 23:51
 * @PackageName:cn.mw.cmdb.service.impl
 * @ClassName: InventoryWorkAssetsServiceImpl
 * @Description: TODD
 * @Version 1.0
 */
@Slf4j
public class InventoryWorkAssetsServiceImpl extends ModelBaseServiceImpl<InventoryWorkAssets, Object> implements InventoryWorkAssetsService {

    public static final String ID = "inventoryAssets";

    private InventoryWorkAssetsMapper inventoryWorkAssetsMapper;

    private InventoryWorkInfoMapper inventoryWorkInfoMapper;

    private ViewManager viewManager;

    private CmdbServiceManage cmdbServiceManage;

    private MwUserApi userApi;

    public InventoryWorkAssetsServiceImpl(InventoryWorkAssetsMapper inventoryWorkAssetsMapper,
                                          InventoryWorkInfoMapper inventoryWorkInfoMapper,
                                          ViewManager viewManager,
                                          CmdbServiceManage cmdbServiceManage,
                                          MwUserApi mwUserApi) {
        this.inventoryWorkAssetsMapper = inventoryWorkAssetsMapper;
        this.inventoryWorkInfoMapper =  inventoryWorkInfoMapper;
        this.viewManager = viewManager;
        this.cmdbServiceManage = cmdbServiceManage;
        this.userApi = mwUserApi;
    }

    @Override
    public InventoryWorkAssets genObject() {
        return new InventoryWorkAssets();
    }

    @Override
    Object doAdd(InventoryWorkAssets param) throws Exception {
        Date nowLocalTime = getNowLocalTime();
        MwUser user = SecurityUtils.getUser();
        String loginName = user.getUsername();
        param.setCreator(loginName);
        param.setModifier(loginName);
        param.setCreateDate(nowLocalTime);
        param.setModificationDate(nowLocalTime);
        inventoryWorkAssetsMapper.save(param);
        return null;
    }

    @Override
    boolean doUpdate(InventoryWorkAssets workAssets) throws Exception {
        MwUser user = SecurityUtils.getUser();
        String loginName = user.getUsername();
        workAssets.setModifier(loginName);
        workAssets.setModificationDate(DateUtils.date());
        Query query = new Query(CriteriaWrapper.where(InventoryWorkAssets::getId).is(workAssets.getId()));
        Update update = CMDBTool.genMongoUpdate(workAssets, null, null);
        CMDBTool.updateFirst(inventoryWorkAssetsMapper, query, update);
        return true;
    }

    @Override
    boolean doRemove(InventoryWorkAssets param) throws Exception {
        return false;
    }

    @Override
    Object doBatchAdd(Object params) throws Exception {
        List<InventoryWorkAssets> paramsList = new ArrayList<>();
        if (params != null && params instanceof List) {
            paramsList = (List<InventoryWorkAssets>) params;
        }
        inventoryWorkAssetsMapper.insertAll(paramsList);
        return true;
    }

    @Override
    boolean doBatchUpdate(Object params) throws Exception {
        return false;
    }

    @Override
    InventoryWorkAssets doSelectById(Object id) throws Exception {
        return null;
    }

    @Override
    List<InventoryWorkAssets> doSelectList(Object param) throws Exception {
        InventoryWorkAssetsParam searchParam = (InventoryWorkAssetsParam) param;
        searchParam.convert();
        searchParam.setIgnoreDataAuth(true);
        Criteria criteria = genCriteria(searchParam);
        List<InventoryWorkAssets> workAssets = CMDBTool.aggregateAuthorizedList(InventoryWorkAssets.class, inventoryWorkAssetsMapper, criteria, searchParam);
        return workAssets;
    }

    @Override
    boolean doBatchRemove(Object params) throws Exception {
        InventoryAssetsIdsParam param =  (InventoryAssetsIdsParam)params;
        CMDBTool.remove(inventoryWorkAssetsMapper, param.getIds(), InventoryWorkAssets.class, true);
        return true;
    }


    @Override
    public void submit(List<InventoryWorkAssets> workAssetsList) throws Exception{
        if(CollUtil.isNotEmpty(workAssetsList)) {
            boolean checkCount=true;
            List<String> imgUlr=new ArrayList<>();
            for (InventoryWorkAssets workAssets : workAssetsList) {
                if (workAssets.getInventoryCount()==null){
                    checkCount=false;
                }
                if (CollUtil.isNotEmpty(workAssets.getImgUrl())){
                    imgUlr.addAll(workAssets.getImgUrl());
                }

                doUpdate(workAssets);
            }
            InventoryWorkAssets workAssets = workAssetsList.get(0);
            Query workAssetsQuery = new Query(CriteriaWrapper.where(InventoryWorkAssets::getInventoryWorkInfoId).is(workAssets.getInventoryWorkInfoId()));
            List<InventoryWorkAssets> list = inventoryWorkAssetsMapper.find(workAssetsQuery);
            for (InventoryWorkAssets inventoryWorkAssets : list) {
                if (inventoryWorkAssets.getInventoryCount() ==null){
                    return;
                }
            }
            List<InventoryCheck.InventoryItem> collect = list.stream().map(w -> new InventoryCheck.InventoryItem(w.getCount(), w.getInventoryCount())).collect(Collectors.toList());

            InventoryCheck.InventoryResultEnum inventoryResultEnum = InventoryCheck.checkInventoryStatus(collect);
            Query workInfoQuery = new Query(CriteriaWrapper.where(InventoryWorkInfo::getId).is(workAssets.getInventoryWorkInfoId()));
            //盘点参数是否填写完整

            Update update = new Update();
            update.set(CMDBTool.getFieldName(InventoryWorkInfo::getInventoryResult), inventoryResultEnum.getCode());
           if (checkCount){
               update.set(CMDBTool.getFieldName(InventoryWorkInfo::getWorkState), WorkStateEnum.SUCCESS.getCode());
           }else {
               update.set(CMDBTool.getFieldName(InventoryWorkInfo::getWorkState), WorkStateEnum.PROCESSING.getCode());
           }
            if (CollUtil.isNotEmpty(imgUlr)){
                update.set(CMDBTool.getFieldName(InventoryWorkInfo::getImgUrl),imgUlr);
            }
            update.set(CMDBTool.getFieldName(InventoryWorkInfo::getActualEndTime),DateUtils.date());
            inventoryWorkInfoMapper.updateFirst(workInfoQuery,update);
        }
    }

    @Override
    public InventoryHistoryDetailsDTO historyDetails(InventorySearchParam param) throws Exception {
        Query infoQuery = new Query(CriteriaWrapper.where(InventoryWorkInfo::getId).is(param.getId()));
        infoQuery.addCriteria(CriteriaWrapper.where(InventoryWorkInfo::getDelFlag).is(false));
        InventoryWorkInfo info = inventoryWorkInfoMapper.findOne(infoQuery);
        if(ObjectUtil.isEmpty(info)){
            throw new BusinessException("inventoryWork-not-found");
        }
        Query query = new Query(CriteriaWrapper.where(InventoryWorkAssets::getInventoryWorkInfoId).is(param.getId()));
        query.addCriteria(CriteriaWrapper.where(InventoryWorkAssets::getDelFlag).is(false));
        List<InventoryWorkAssets> list = inventoryWorkAssetsMapper.find(query);
        List<String> assetsIdList = info.getAssetsIdList();
        InstanceServiceImpl instanceService = (InstanceServiceImpl) cmdbServiceManage.getModelService(InstanceServiceImpl.ID);
        if(CollUtil.isNotEmpty(assetsIdList)){
            InstanceSearchParam instanceSearchParam = new InstanceSearchParam();
            instanceSearchParam.setInstanceIds(assetsIdList);
            List<InstanceInfoExtend> instanceInfoExtends = instanceService.aggregateList(instanceSearchParam);
            Map<String, InstanceInfoExtend> instanceInfoExtendMap = instanceInfoExtends.stream().collect(Collectors.toMap(InstanceInfoExtend::getId, Function.identity()));
            List<Long> userIds = instanceInfoExtends.stream().map(DataAuth::getUsers).filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());
            List<Long> orgIds = instanceInfoExtends.stream().map(DataAuth::getOrgIds).filter(Objects::nonNull).flatMap(Collection::stream).collect(Collectors.toList());

            Map<Long, MwUser> userMap = new HashMap<>();
            Map<Long, OrgDTO> orgMap = new HashMap<>();
            if (CollUtil.isNotEmpty(userIds)) {
                userMap = Optional.ofNullable(userApi.getUsersByUserIds(userIds))
                        .orElse(Collections.emptyList())
                        .stream()
                        .collect(Collectors.toMap(MwUser::getId, Function.identity()));
            }
            if (CollUtil.isNotEmpty(orgIds)) {
                orgMap = Optional.ofNullable(userApi.getOrgListByOrgIds(orgIds))
                        .orElse(Collections.emptyList())
                        .stream()
                        .collect(Collectors.toMap(OrgDTO::getId, Function.identity()));
            }
            for (InventoryWorkAssets workAsset : list) {
                String assetId = workAsset.getAssetsId();
                InstanceInfoExtend matchingInstance = instanceInfoExtendMap.get(assetId);

                if (matchingInstance != null) {
                    Object brand = matchingInstance.getData().get(MW_ASSETBRAND);
                    Object productId = matchingInstance.getData().get(MW_PRODUCT_ID);
                    Object assetCoding = matchingInstance.getData().get(MW_ASSET_CODING);
                    Object assetStatus = matchingInstance.getData().get(MW_ASSET_STATUS);
                    Object specifications = matchingInstance.getData().get(MW_SPECIFICATIONS);
                    Map<Long, MwUser> finalUserMap = userMap;
                    String principal = Optional.ofNullable(matchingInstance.getUsers())
                            .map(users -> users.stream().map(userId -> Optional.ofNullable(finalUserMap.get(userId)).map(MwUser::getNickName).orElse("")).collect(Collectors.joining(","))).orElse("");
                    Map<Long, OrgDTO> finalOrgMap = orgMap;
                    String orgName = Optional.ofNullable(matchingInstance.getOrgIds())
                            .map(orgIdsList -> orgIdsList.stream().map(orgId -> Optional.ofNullable(finalOrgMap.get(orgId)).map(OrgDTO::getOrgName).orElse("")).collect(Collectors.joining(","))).orElse("");
                    workAsset.setInstanceName(matchingInstance.getInstanceName());
                    workAsset.setModelGroupName(matchingInstance.getModelGroupName());
                    workAsset.setBrand(ObjectUtil.isNotEmpty(brand) ? JSON.toJSONString(brand) : "");
                    workAsset.setProductId(ObjectUtil.isNotEmpty(productId) ? String.valueOf(productId) : "");
                    workAsset.setUsers(matchingInstance.getUsers());
                    workAsset.setPrincipal(principal);
                    workAsset.setOrgIds(matchingInstance.getOrgIds());
                    workAsset.setOrgName(orgName);
                    workAsset.setAssetCoding(ObjectUtil.isNotEmpty(assetCoding) ? String.valueOf(assetCoding) : "");
                    workAsset.setAssetStatus(ObjectUtil.isNotEmpty(assetStatus) ? String.valueOf(assetStatus) : "");
                    workAsset.setSpecifications(ObjectUtil.isNotEmpty(specifications) ? String.valueOf(specifications) : "");
                }
            }
        }
        AtomicInteger surplusCount = new AtomicInteger(0);
        AtomicInteger shortageCount = new AtomicInteger(0);
        AtomicInteger matchCount = new AtomicInteger(0);

        list.forEach(workAssets -> {
            if (workAssets.getInventoryCount() > workAssets.getCount()) {
                workAssets.setResult(InventoryCheck.InventoryResultEnum.SURPLUS.getCode());
                surplusCount.getAndIncrement();
            } else if (workAssets.getInventoryCount() < workAssets.getCount()) {
                workAssets.setResult(InventoryCheck.InventoryResultEnum.SHORTAGE.getCode());
                shortageCount.getAndIncrement();
            } else {
                workAssets.setResult(InventoryCheck.InventoryResultEnum.MATCH.getCode());
                matchCount.getAndIncrement();
            }
        });

        InventoryHistoryDetailsDTO dto = new InventoryHistoryDetailsDTO();
        dto.setSurplusNum(surplusCount.getAndIncrement());
        dto.setShortageNum(shortageCount.getAndIncrement());
        dto.setMatchNum(matchCount.getAndIncrement());
        MWPage pageRet = (MWPage)viewManager.getListViewObject(list, InventoryWorkAssets.class, param, new ViewConvertContext());
        dto.setInventoryWorkAssetsPage(pageRet);
        dto.setInventoryWorkInfo(info);
        return dto;
    }
}
